package edu.pku.sei.pgie.analyzer.internal;

import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.Stack;

import edu.pku.sei.pgie.analyzer.core.Token;

import org.eclipse.jdt.core.ISourceReference;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.QualifiedType;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SimpleType;
import org.eclipse.jdt.core.dom.TypeDeclaration;

/**
 * @deprecated
 * @author Stack
 *
 */
public class Tokenizer {
	HashMap fqnTokens;
	HashMap fqnContent;
	String sourceCode;
	boolean cached;

	CompilationUnit root;

	public Tokenizer() {
		this.fqnTokens = new HashMap();
		this.fqnContent = new HashMap();
	}

	public CompilationUnit getRoot() {
		return root;
	}

	public void setRoot(CompilationUnit newRoot) throws JavaModelException {
		if(newRoot == null) {
			this.cached = false;
			this.clear();
			return;
		}
		if(root != null && newRoot == root) {
			return;
		}
		this.root = newRoot;
		this.cached = false;
		this.clear();
		if(this.root.getJavaElement() instanceof ISourceReference) {
			ISourceReference reference = (ISourceReference) root.getJavaElement();
			this.sourceCode = reference.getSource();
		}
	}

	private void clear() {
		this.fqnContent.clear();
		this.fqnTokens.clear();
	}

	public String getSourceCode() {
		return this.sourceCode;
	}

	public String getSourceByFqn(String fqn) {
		if(!this.cached)
			this.tokenize();
		return (String) this.fqnContent.get(fqn);
	}


	public List getTokenByFqn(String fqn) {
		if(!this.cached)
			this.tokenize();
		return (List) this.fqnTokens.get(fqn);
	}

	public String[] getFqns() {
		if(!this.cached)
			this.tokenize();
		Set fqnSet = fqnTokens.keySet();
		String[] fqns = new String[fqnSet.size()];
		Iterator iterator = fqnSet.iterator();
		int i = 0;
		while(iterator.hasNext()) {
			fqns[i++] = (String) iterator.next();
		}
		return fqns;
	}


	protected String getLexicalToken(int start,int length) {
		return this.sourceCode.substring(start,start + length);
	}

	class TokenizeVisitor extends ASTVisitor {
		Stack typeDeclarationStack;
		List commonTokens;
		public TokenizeVisitor() {
			typeDeclarationStack = new Stack();
			commonTokens = new LinkedList();
		}

		protected void addToken(int offset,int length) {
			Token tokenObj = new Token();
			tokenObj.token = getLexicalToken(offset,length);
			tokenObj.position = offset;
			if(typeDeclarationStack.isEmpty()) {
				commonTokens.add(tokenObj);
				return;
			}
			Iterator iterator = typeDeclarationStack.iterator();
			while(iterator.hasNext()) {
				String fqn = (String) iterator.next();
				List tokenList = (List) fqnTokens.get(fqn);
				tokenList.add(tokenObj);
			}
		}

		public void endVisit(CompilationUnit node) {
			Set fqnSet = fqnTokens.keySet();
			Iterator iterator = fqnSet.iterator();
			while(iterator.hasNext()) {
				String fqn = (String) iterator.next();
				List tokens = (List) fqnTokens.get(fqn);
				Iterator commonItr = commonTokens.iterator();
				while(commonItr.hasNext()) {
					tokens.add(commonItr.next());
				}
			}
		}

		public void endVisit(TypeDeclaration node) {
			ITypeBinding typeBinding = node.resolveBinding();
			if(!this.typeDeclarationStack.isEmpty())
				this.typeDeclarationStack.pop();
		}

		public boolean visit(QualifiedName node) {
			this.addToken(node.getStartPosition(),node.getLength());
			return false;
		}

		public boolean visit(SimpleName node) {
			this.addToken(node.getStartPosition(),node.getLength());
			return true;
		}

		public boolean visit(QualifiedType node) {
			this.addToken(node.getStartPosition(),node.getLength());
			return false;
		}

		public boolean visit(SimpleType node) {
			this.addToken(node.getStartPosition(),node.getLength());
			return true;
		}

		public boolean visit(TypeDeclaration node) {
			ITypeBinding binding = node.resolveBinding();
			String fqn = binding.getQualifiedName();
			if(fqnTokens.get(fqn) == null) {
				fqnTokens.put(fqn,new LinkedList());
			}
			typeDeclarationStack.push(fqn);
			fqnContent.put(fqn,getLexicalToken(node.getStartPosition(),node.getLength()));
			return true;
		}
	}

	protected void tokenize() {
		if(root == null || this.cached)
			return;
		ASTVisitor visitor = new TokenizeVisitor();
		root.accept(visitor);
		this.cached = true;
	}
}
